package com.github.aloxc.plugin.restplus.common;



import com.github.aloxc.plugin.restplus.annotations.*;
import com.github.aloxc.plugin.restplus.jaxrs.JaxrsAnnotationHelper;
import com.github.aloxc.plugin.restplus.micronaut.MicronautAnnotationHelper;
import com.github.aloxc.plugin.restplus.spring.SpringRequestMappingAnnotationHelper;
import com.github.aloxc.plugin.restplus.method.Parameter;
import com.github.aloxc.plugin.restplus.utils.JsonUtils;

import com.github.aloxc.plugin.restplus.utils.StringUtils;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.github.aloxc.plugin.restplus.annotations.SpringRequestParamAnnotations.*;

/**
 * PsiMethod处理类
 * @author leero
 */
public class PsiMethodHelper {
    PsiMethod psiMethod;
    Project myProject;
    Module myModule;
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
    private static Matcher matcher;


    public static PsiMethodHelper create(@NotNull PsiMethod psiMethod) {
        return new PsiMethodHelper(psiMethod);
    }

    public PsiMethodHelper withModule(Module module) {
        this.myModule = module;
        return this;
    }

    protected PsiMethodHelper(PsiMethod psiMethod) {
        this.psiMethod = psiMethod;
    }

    @NotNull
    protected Project getProject() {
        myProject = psiMethod.getProject();
        return myProject;
    }

    /**
     * 替换字符串占位符, 字符串中使用${key}表示占位符
     *
     * @param sourceString 需要匹配的字符串，示例："名字:${name},年龄:${age},学校:${school}";
     * @param param        参数集,Map类型
     * @return
     */
    public static String replaceWithMap(String sourceString, Map<String, Object> param) {
        if (StringUtils.isNullOrEmpty(sourceString) || param == null || param.size() == 0) {
            return sourceString;
        }
        String targetString = sourceString;
        matcher = PATTERN.matcher(sourceString);
        Set<String> used = new HashSet<>();
        while (matcher.find()) {
            try {
                String key = matcher.group();
                //如果占位符是{} 这里就是key.substring(1, key.length() - 1).trim()
                String keyclone = key.substring(1, key.length() - 1).trim();
                Object value = param.get(keyclone);
                if (value != null) {
                    targetString = targetString.replace(key, value.toString());
                    used.add(keyclone);
                }
            } catch (Exception e) {
                throw new RuntimeException("String formatter failed["+sourceString+"],["+ JsonUtils.toJson(param,true) +"]", e);
            }
        }
        used.stream().forEach(it->{
            param.remove(it);
        });
        return targetString;
    }

    /**
     * 获取方法中基础类型（primitive和string、date等以及这些类型数组）
     */
    @NotNull
    public Map<String, Object> buildParam() {
        List<Parameter> parameterList = getParameterList();

        Map<String, Object> baseTypeParamMap = new LinkedHashMap();

        // 拼接参数
        for (Parameter parameter : parameterList) {
//跳过标注 RequestBody 注解的参数
            if (parameter.isRequestBodyFound()) {
                continue;
            }

            // todo 判断类型
            // 8 PsiPrimitiveType
            // 8 boxed types; String,Date:PsiClassReferenceType == field.getType().getPresentableText()
            String shortTypeName = parameter.getShortTypeName();
            Object defaultValue = PsiClassHelper.getJavaBaseTypeDefaultValue(shortTypeName);
            //简单常用类型
            if (defaultValue != null) {
                baseTypeParamMap.put(parameter.getParamName(), (defaultValue));
                continue;
            }

            PsiClassHelper psiClassHelper = PsiClassHelper.create(psiMethod.getContainingClass());
            PsiClass psiClass = psiClassHelper.findOnePsiClassByClassName(parameter.getParamType(), getProject());

            if (psiClass != null) {
                PsiField[] fields = psiClass.getFields();
                for (PsiField field : fields) {
                    if (field.getModifierList().hasModifierProperty("static")) {
                        continue;
                    }
                    Object fieldDefaultValue = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if (fieldDefaultValue != null) {
                        baseTypeParamMap.put(field.getName(), fieldDefaultValue);
                    }
                }
            }

          /*  PsiClass psiClass2 = psiClassHelper.findOnePsiClassByClassName2(parameter.getParamType(), getProject());
            if (psiClass2 != null) {
                PsiField[] fields = psiClass2.getFields();
                for (PsiField field : fields) {
                    Object fieldDefaultValue  = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if(fieldDefaultValue != null)
                        baseTypeParamMap.put(field.getName(), fieldDefaultValue);
                }
            }*/
        }
        return baseTypeParamMap;
    }
    /*获取方法中基础类型（primitive和string、date等以及这些类型数组）*/
    @NotNull
    public Map<String, Object> getBaseTypeParameterMap() {
        List<Parameter> parameterList = getParameterList();

        Map<String,Object> baseTypeParamMap = new LinkedHashMap();

        // 拼接参数
        for (Parameter parameter : parameterList) {
//跳过标注 RequestBody 注解的参数
            if (parameter.isRequestBodyFound()) {
                continue;
            }

            // todo 判断类型
            // 8 PsiPrimitiveType
            // 8 boxed types; String,Date:PsiClassReferenceType == field.getType().getPresentableText()
            String shortTypeName = parameter.getShortTypeName();
            Object defaultValue = PsiClassHelper.getJavaBaseTypeDefaultValue(shortTypeName);
            //简单常用类型
            if (defaultValue != null) {
                baseTypeParamMap.put(parameter.getParamName(),(defaultValue));
                continue;
            }

            PsiClassHelper psiClassHelper = PsiClassHelper.create(psiMethod.getContainingClass());
            PsiClass psiClass = psiClassHelper.findOnePsiClassByClassName(parameter.getParamType(), getProject());

            if (psiClass != null) {
                PsiField[] fields = psiClass.getFields();
                for (PsiField field : fields) {
                    Object fieldDefaultValue  = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if(fieldDefaultValue != null) {
                        baseTypeParamMap.put(field.getName(), fieldDefaultValue);
                    }
                }
            }

          /*  PsiClass psiClass2 = psiClassHelper.findOnePsiClassByClassName2(parameter.getParamType(), getProject());
            if (psiClass2 != null) {
                PsiField[] fields = psiClass2.getFields();
                for (PsiField field : fields) {
                    Object fieldDefaultValue  = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if(fieldDefaultValue != null)
                        baseTypeParamMap.put(field.getName(), fieldDefaultValue);
                }
            }*/
        }
        return baseTypeParamMap;
    }

    @NotNull
    public List<Parameter> getParameterList() {
        List<Parameter> parameterList = new ArrayList<>();

        PsiParameterList psiParameterList = psiMethod.getParameterList();
        PsiParameter[] psiParameters = psiParameterList.getParameters();
        for (PsiParameter psiParameter : psiParameters) {
            //忽略 request response

            String paramType = psiParameter.getType().getCanonicalText();
            if ("javax.servlet.http.HttpServletRequest".equals(paramType)
                    || "javax.servlet.http.HttpServletResponse".equals(paramType)) {
                continue;
            }
            //必传参数 @RequestParam
            PsiModifierList modifierList = psiParameter.getModifierList();
            boolean requestBodyFound = modifierList.findAnnotation(REQUEST_BODY.getQualifiedName()) != null;
            // 没有 RequestParam 注解, 有注解使用注解value
            String paramName = psiParameter.getName();
            String requestName = null;


            PsiAnnotation pathVariableAnno = modifierList.findAnnotation(PATH_VARIABLE.getQualifiedName());
            if (pathVariableAnno != null) {
                requestName = getAnnotationValue(pathVariableAnno);
                Parameter parameter = new Parameter(paramType, requestName != null ? requestName : paramName).setRequired(true).requestBodyFound(requestBodyFound);
                parameterList.add(parameter);
            }

            PsiAnnotation requestParamAnno = modifierList.findAnnotation(REQUEST_PARAM.getQualifiedName());
            if (requestParamAnno != null) {
                requestName = getAnnotationValue(requestParamAnno);
                Parameter parameter = new Parameter(paramType, requestName != null ? requestName : paramName).setRequired(true).requestBodyFound(requestBodyFound);
                parameterList.add(parameter);
            }
            PsiAnnotation requestHeaderAnno = modifierList.findAnnotation(SpringRequestParamAnnotations.REQUEST_HEADER.getQualifiedName());

            if (pathVariableAnno == null && requestParamAnno == null && requestHeaderAnno == null) {
                Parameter parameter = new Parameter(paramType, paramName).requestBodyFound(requestBodyFound);
                parameterList.add(parameter);
            }
        }
        return parameterList;
    }

    public String getAnnotationValue(PsiAnnotation annotation) {
        String paramName = null;
        PsiAnnotationMemberValue attributeValue = annotation.findDeclaredAttributeValue("value");

        if (attributeValue != null && attributeValue instanceof PsiLiteralExpression) {
            paramName = (String) ((PsiLiteralExpression) attributeValue).getValue();
        }
        return paramName;
    }


    public List<Parameter> getRequestHeaderList() {
        List<Parameter> headerList = new ArrayList<>();

        PsiParameterList psiParameterList = this.psiMethod.getParameterList();
        PsiParameter[] psiParameters = psiParameterList.getParameters();
        for (PsiParameter psiParameter : psiParameters) {
            String paramType = psiParameter.getType().getCanonicalText();
            if (!"javax.servlet.http.HttpServletRequest".equals(paramType) &&
                    !"javax.servlet.http.HttpServletResponse".equals(paramType)) {
                PsiModifierList modifierList = psiParameter.getModifierList();
                PsiAnnotation requestHeaderAnno = modifierList.findAnnotation(SpringRequestParamAnnotations.REQUEST_HEADER.getQualifiedName());
                if (requestHeaderAnno != null) {
                    String paramName = psiParameter.getName();
                    String requestName = getAnnotationValue(requestHeaderAnno);
                    Parameter parameter = new Parameter(paramType, (requestName != null) ? requestName : paramName);
                    headerList.add(parameter);
                }
            }
        }
        return headerList;
    }

    public Map<String, Object> buildHeader() {
        List<Parameter> headerList = getRequestHeaderList();

        Map<String, Object> baseTypeHeaderMap = new LinkedHashMap();

        // 拼接参数
        for (Parameter header : headerList) {
//跳过标注 RequestBody 注解的参数
            if (header.isRequestBodyFound()) {
                continue;
            }

            // todo 判断类型
            // 8 PsiPrimitiveType
            // 8 boxed types; String,Date:PsiClassReferenceType == field.getType().getPresentableText()
            String shortTypeName = header.getShortTypeName();
            Object defaultValue = PsiClassHelper.getJavaBaseTypeDefaultValue(shortTypeName);
            //简单常用类型
            if (defaultValue != null) {
                baseTypeHeaderMap.put(header.getParamName(), (defaultValue));
                continue;
            }

            PsiClassHelper psiClassHelper = PsiClassHelper.create(psiMethod.getContainingClass());
            PsiClass psiClass = psiClassHelper.findOnePsiClassByClassName(header.getParamType(), getProject());

            if (psiClass != null) {
                PsiField[] fields = psiClass.getFields();
                for (PsiField field : fields) {
                    if (field.getModifierList().hasModifierProperty("static")) {
                        continue;
                    }
                    Object fieldDefaultValue = PsiClassHelper.getJavaBaseTypeDefaultValue(field.getType().getPresentableText());
                    if (fieldDefaultValue != null) {
                        baseTypeHeaderMap.put(field.getName(), fieldDefaultValue);
                    }
                }
            }
        }
        return baseTypeHeaderMap;
    }


    /**
     * 构建RequestBody json 参数
     *
     * @param parameter
     * @return
     */
    public String buildRequestBodyJson(Parameter parameter) {
//        JavaFullClassNameIndex.getInstance();

        Project project = psiMethod.getProject();
        final String className = parameter.getParamType();

        String queryJson = PsiClassHelper.create(psiMethod.getContainingClass()).withModule(myModule).convertClassToJson(className, project);
        return queryJson;
    }


    public String buildRequestBodyJson() {
        List<Parameter> parameterList = this.getParameterList();
        for (Parameter parameter : parameterList) {
            if (parameter.isRequestBodyFound()) {
                return buildRequestBodyJson(parameter);
            }
        }
        return null;
    }

    @NotNull
    public String buildServiceUriPath() {
        String ctrlPath = null;
        String methodPath = null;

        //判断rest服务提供方式 spring or jaxrs
        PsiClass containingClass = psiMethod.getContainingClass();
        RestSupportedAnnotationHelper annotationHelper;
        if (isSpringRestSupported(containingClass)) {
            ctrlPath = SpringRequestMappingAnnotationHelper.getOneRequestMappingPath(containingClass);
            methodPath = SpringRequestMappingAnnotationHelper.getOneRequestMappingPath(psiMethod);
        } else if (isJaxrsRestSupported(containingClass)) {
            ctrlPath = JaxrsAnnotationHelper.getClassUriPath(containingClass);
            methodPath = JaxrsAnnotationHelper.getMethodUriPath(psiMethod);
        } else if (isMicronautSupported(containingClass)) {
            ctrlPath = MicronautAnnotationHelper.getOneRequestMappingPath(containingClass);
            methodPath = MicronautAnnotationHelper.getOneRequestMappingPath(psiMethod);
        }
        if(methodPath == null){
            methodPath = "";
        }
        if (ctrlPath == null) {
            return null;
        }

        if (!ctrlPath.startsWith("/")) {
            ctrlPath = "/".concat(ctrlPath);
        }
        if (!ctrlPath.endsWith("/")) {
            ctrlPath = ctrlPath.concat("/");
        }
        if (methodPath.startsWith("/")) {
            methodPath = methodPath.substring(1, methodPath.length());
        }

        return ctrlPath + methodPath;
    }

    private boolean isMicronautSupported(PsiClass containingClass) {
        PsiModifierList modifierList = containingClass.getModifierList();
        return modifierList.findAnnotation(MicronautRequestAnnotation.CONTROLLER.getQualifiedName()) != null;
    }

    @NotNull
    public String buildServiceUriPathWithParams() {
        String serviceUriPath = buildServiceUriPath();

        Map<String, Object> paramMap = buildParam();
        String params = null;
        if (paramMap == null || paramMap.size() == 0) {
            params = "";
        } else {
            StringBuffer buff = new StringBuffer();
            paramMap.forEach((s, o) -> buff.append(s).append("=").append(o).append("&"));
            params = (buff.length() > 0) ? buff.deleteCharAt(buff.length() - 1).toString() : "";
        }
        // RequestMapping 注解设置了 param

        if (!params.isEmpty()) {
            serviceUriPath = replaceWithMap(serviceUriPath, paramMap);
            if(!paramMap.isEmpty()) {
                StringBuilder urlBuilder = new StringBuilder(serviceUriPath);
                return urlBuilder.append(serviceUriPath.contains("?") ? "&" : "?").append(params).toString();
            }
        }
        return serviceUriPath;
    }

    /**
     * 包含 "RestController" "Controller"
     */
    public static boolean isSpringRestSupported(PsiClass containingClass) {
        PsiModifierList modifierList = containingClass.getModifierList();

        return modifierList.findAnnotation(SpringControllerAnnotation.REST_CONTROLLER.getQualifiedName()) != null ||
                modifierList.findAnnotation(SpringControllerAnnotation.CONTROLLER.getQualifiedName()) != null;
    }

    public static boolean isJaxrsRestSupported(PsiClass containingClass) {
        PsiModifierList modifierList = containingClass.getModifierList();

        return modifierList.findAnnotation(JaxrsRequestAnnotation.PATH.getQualifiedName()) != null;
    }

    /**
     * 是否是restFul方法
     * @param psiMethod
     * @return
     */
    public static boolean isRestfulMethod(PsiMethod psiMethod) {
        PsiAnnotation[] annotations = psiMethod.getModifierList().getAnnotations();

        for (PsiAnnotation annotation : annotations) {
            boolean match = Arrays.stream(SpringRequestMethodAnnotation.values()).map(sra -> sra.getQualifiedName()).anyMatch(name -> name.equals(annotation.getQualifiedName()));
            if(match) {
                return match;
            }
        }

        for (PsiAnnotation annotation : annotations) {
            boolean match = Arrays.stream(JaxrsHttpMethodAnnotation.values()).map(sra -> sra.getQualifiedName()).anyMatch(name -> name.equals(annotation.getQualifiedName()));
            if(match) {
                return match;
            }
        }
        for (PsiAnnotation annotation : annotations) {
            boolean match = Arrays.stream(MicronautRequestMethodAnnotation.values()).map(sra -> sra.getQualifiedName()).anyMatch(name -> name.equals(annotation.getQualifiedName()));
            if(match) {
                return match;
            }
        }
        return false;
    }


    /**
     * 是否是restFul方的注解
     * @param psiMethod
     * @return
     */
    public static boolean isRestfulMethodAnnotation(PsiMethod psiMethod,PsiAnnotation annotation) {
            boolean match = Arrays.stream(SpringRequestMethodAnnotation.values()).map(sra -> sra.getQualifiedName()).anyMatch(name -> name.equals(annotation.getQualifiedName()));
            if(match) {
                return match;
            }
            match = Arrays.stream(JaxrsHttpMethodAnnotation.values()).map(sra -> sra.getQualifiedName()).anyMatch(name -> name.equals(annotation.getQualifiedName()));
            if(match) {
                return match;
            }
            match = Arrays.stream(MicronautRequestMethodAnnotation.values()).map(sra -> sra.getQualifiedName()).anyMatch(name -> name.equals(annotation.getQualifiedName()));
            if(match) {
                return match;
            }
        return false;
    }

//    /**
//     *
//     * @param qualifiedName
//     * @return
//     */
//    public static String getRequestMethod(String qualifiedName){
//
//    }
}
